/**
 * Copyright (c) 2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// NOTE autogenerated file

let __fail_count = 0
let __fail_description: string = ""
const __max_errors = 5

let __warn_action: int = 0 // -1 = silence, 0 = warn, 1 = err

function __fail(): void {
  console.println("test failed {");
  console.log(__fail_description)
  console.println("}");
  if (__fail_count >= __max_errors) {
    console.println("terminating after max (" + __max_errors + ") errors");
    arktest.assertTrue(false);
  }
  __fail_count += 1;
}

class ArrayValueIterator implements IterableIterator<NullishType> {
  private idx: int
  private val: ArrayValue

  constructor(val: ArrayValue) {
    this.idx = 0
    this.val = val
  }

  override $_iterator(): IterableIterator<NullishType> {
    return this;
  }

  override next(): IteratorResult<NullishType> {
    if (this.idx >= this.val.getLength()) {
      return new IteratorResult<NullishType>()
    }
    return new IteratorResult<NullishType>(this.val.getElement(this.idx++).getData());
  }
}

function __value_rewrap_builtin_array(a: NullishType): NullishType {
  if (!(Type.of(a) instanceof ArrayType)) {
    return a;
  }
  return new ArrayValueIterator(reflect.Value.of(a) as ArrayValue)
}

class IterableData {
  cls: ClassType
  meth: Method
  constructor(cls: ClassType, meth: Method) {
    this.cls = cls
    this.meth = meth
  }
}
const KNOWN_ITERABLES = new Array<IterableData>()

function getIteratorFromIterable(a: NullishType, aTypeUncasted: Type): Iterator<NullishType> | undefined {
  if (!(aTypeUncasted instanceof ClassType)) {
    return undefined
  }

  const aType = aTypeUncasted as ClassType

  for (let i = 0; i < KNOWN_ITERABLES.length; i++) {
    const ki = KNOWN_ITERABLES[i];
    if (ki.cls.equals(aType)) {
      return ki.meth.invoke(a, [])! as Iterator<NullishType>
    }
  }
  const mNum = aType.getMethodsNum();
  for (let i = 0; i < mNum; i++) {
    const m = aType.getMethod(i);
    if (m.getName() == "$_iterator" && !m.isStatic() && m.getType().getParametersNum() == 0) {
      KNOWN_ITERABLES.push(new IterableData(aType, m));
      return m.invoke(a, [])! as Iterator<NullishType>
    }
  }
  return undefined;
}

function __value_is_same(a: NullishType, b: NullishType): boolean {
  if (Runtime.sameValue(a, b)) {
    return true;
  }
  if (a instanceof Number && b instanceof Number) {
      let a1: number = a as Object as Number
      let b1: number = b as Object as Number
      if (isNaN(a1) != isNaN(b1)) {
        return false
      }
      if (!isNaN(a1) && a1 != b1) {
        if (Double.compare(a1, b1)) {
          if (__warn_action == -1) {
            return true;
          }
          if (__warn_action == 1) {
            console.log("treating warning as error")
            return false;
          }
          console.log("warning {")
          console.log(__fail_description)
          console.log("precision comparison")
          console.log(a1 + "(got) vs (exp)" + b1)
          console.log("}")
          return true
        }
        return false
      }
      return true
  }

  if (a instanceof DataView) {
      const x = a as DataView
      const arr = new Array<number>()
      for (let i: int = 0; i < x.byteLength; i++) {
        arr.push(x.getUint8(i))
      }
      __fail_description += '\ndvw ' + arr + ' <- converted data view'
      a = arr
  }

  a = __value_rewrap_builtin_array(a);
  b = __value_rewrap_builtin_array(b);

  const aType = Type.of(a)
  const bType = Type.of(b)

  const aItMb = getIteratorFromIterable(a, aType);
  const bItMb = getIteratorFromIterable(b, bType);
  if (aItMb != undefined && bItMb != undefined) {
    const aIt = aItMb;
    const bIt = bItMb;
    while (true) {
        const an = aIt.next();
        const bn = bIt.next();
        if (an.done != bn.done) {
          return false;
        }
        if (an.done) {
            break;
        }

        if (!__value_is_same(an.value, bn.value)) {
          return false;
        }
    }
    return true;
  }

  return false
}

function __check_value(a: NullishType, b: NullishType): void {
  if (__value_is_same(a, b)) {
    return
  }

  __fail_description += "\ngot " + reflect.Value.of(a).toPrint(10) + " of type " + Type.of(a).toString();
  __fail_description += "\nexp " + reflect.Value.of(b).toPrint(10) + " of type " + Type.of(b).toString();

  __fail();
}

<%= test_cases_current_chunk[0]&.conf&.top_scope %>

% slices = []
% test_cases_current_chunk.zip((0..)).each_slice(10).with_index { |test_case_cur_slice, slice_idx|
%   slices.push slice_idx
function test<%= slice_idx %>(): void {
%   test_case_cur_slice.each { |test_case_cur_slice_data|
%     test_case_cur, idx = test_case_cur_slice_data
  {
    // <%= idx %>
%     warn_level = test_case_cur.conf.special.flat_map { |s|
%       if s.match =~ test_case_cur.ts.expr
%         [s.action]
%       else
%         []
%       end
%     }.max || 0
    __warn_action = <%= warn_level %>
    __fail_description = <%= "#{test_case_cur.ts.expr}".inspect %>
%     if test_case_cur.self
    let self = <%= test_case_cur.ts.self %>;
%     end
    <%= test_case_cur.conf.setup %>
%     rb_obj = expected[(idx * 2).to_s]
%     if rb_obj.kind_of?(String) && rb_obj.start_with?("#__threw ")
    let threw = false;
    try {
      <%= test_case_cur.ts.expr %>;
    } catch (e) {
      threw = true;
    }
    if (!threw) {
      __fail_description += "\nexpected to throw ; ts description : " + <%= rb_obj.dump %>
      __fail();
    }
%     elsif test_case_cur.conf.ret_type == "void"
    <%= test_case_cur.ts.expr %>
%     else
    __check_value(<%= test_case_cur.ts.expr %>, <%= ESChecker::ValueDumper.new(test_case_cur.conf).dump(rb_obj) %>);
%     end
%   if test_case_cur.self
%     rb_obj = expected[(idx * 2 + 1).to_s]
    __check_value(self, <%= ESChecker::ValueDumper.new(test_case_cur.conf).dump(rb_obj, is_self: true) %>);
%   end
  }
%   }
}

% }

function main(): void {
% slices.each { |i|
  test<%= i %>();
% }
  arktest.assertEQ(__fail_count, 0);
}
